#ifndef _Core_H_
#define _Core_H_

#include "Vtop.h"

#include "core/Trap.h"
#include "core/Commit.h"
#include "core/ITrace.h"
#include "core/VTrace.h"
#include "core/Register.h"
#include "core/WatchPoint.h"
#include "core/difftest.h"
#include "device/axi4/axi4_xbar.hpp"

#include <cstddef>
#include <cstdint>
#include <deque>
#include <functional>
#include <filesystem>

#define RESET_VECTOR 0x80000000ul

class Core {
public:
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    /* ************* trace ************* */ 
    IFDEF(CONFIG_ITRACE, ITrace* itrace);
    IFDEF(CONFIG_VTRACE, VTrace* vtrace);
    /* ************* trace ************* */ 

    /* ************* AXI4 ************* */ 
    axi4_ptr <32,64,4> master_ptr;
    axi4_ref <32,64,4>* master_ref;
    axi4     <32,64,4> joint;
    axi4_ref <32,64,4> joint_ref { joint };
    axi4_xbar<32,64,4>* xbar;
    void posedge();
    void negedge();
    void connect_wire(axi4_ptr<32,64,4> &master_ptr, Vtop* top);
    void set_xbar(axi4_xbar<32,64,4>* xbar);
    /* ************* AXI4 ************* */ 

    word_t pc;
    GPR gpr;
    CSR csr;
    VerilatedContext* ctx { nullptr };
    Vtop* top { nullptr };
    void one_cycle();
    size_t cycle_count { 0 };
    size_t inst_count { 0 };
    uint32_t last_inst { 0 };
    uint64_t last_pc { 0 };
    uint64_t exec_one_inst(Commit& commit);
    void reset(int n);

#ifdef CONFIG_DIFFTEST
    DifftestContext* dut_ctx;
    DifftestContext* ref_ctx;
    bool is_skip_ref = false;
    std::deque<word_t> intr_event_queue;
    void difftest_init(const char *ref_file, uint8_t *mem_base, size_t mem_size);
    void difftest_step(word_t pc);
    void difftest_raise_intr(uint64_t NO);
    void difftest_skip_ref();
    void difftest_check(word_t pc, DifftestContext* ref, DifftestContext* dut);
#endif

#ifdef CONFIG_WATCHPOINT
    WatchPoint watchpoint;
    bool stop_flag { false };
#endif

    Core();
    ~Core();
    void step(bool msip, bool mtip);
    void take_trap(Trap trap_cause, word_t trap_pc);
    void open_trace(const char* path);
    void dump_regs();
};

#endif
